﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace BankerAlgorithm
{
    public struct SysRes
    {
        /***
         * 系统资源3类，第一类有4个，第二类有3个，第三类有5个
         * （4，3，5）
         * 有3个进程
         * 第一个进程需要（2，2，3）
         * 第二个进程需要（3，3，3）
         * 第三个进程需要（4，1，2）
         * 第四个进程需要（1，1，2）
         * 第五个进程需要（4，3，5）
         */
        /// <summary>
        /// 系统资源
        /// </summary>
        public static int[] Available = new int[] { 4, 3, 5 };
        /// <summary>
        /// 最大需求矩阵
        /// </summary>
        public static int[,] Max = new int[,] {
           {4,3,5},
           {3,3,3},
           {4,1,2},
           {1,1,2},
           {4,3,5}
        };
        /// <summary>
        /// 分配矩阵
        /// </summary>
        public static int[,] Allocation = new int[,] {
            {0,0,0},
            {0,0,0},
            {0,0,0},
            {0,0,0},
            {0,0,0}
        };
        /// <summary>
        /// 需求矩阵
        /// </summary>
        public static int[,] Need = new int[,] {
           {0,0,0 },
           {0,0,0 },
           {0,0,0 },
           {0,0,0 },
           {0,0,0 },
        };
        /// <summary>
        /// 请求矩阵
        /// </summary>
        public static int[,] Request = new int[,] {
           {0,0,0},
           {0,0,0},
           {0,0,0},
           {0,0,0},
           {0,0,0},
        };

    }
    class Program
    {


        static void Main(string[] args)
        {
            //进程队列
            List<int> processList = new List<int>();
            Console.WriteLine("《银行家算法》");
            Console.WriteLine("-----------------");
            //查看分配前系统资源数
            LookAvailable("默认系统资源");
            //获取进程数
            int processNumber = SysRes.Max.GetLength(0);
            //获取进程需要的资源数
            int proResNum = SysRes.Max.GetLength(1);
            InitData(processNumber, proResNum);//初始化分配
            //查看分配后系统资源数
            LookAvailable("初始化分配后系统资源");
            //查看资源分配
            LookAllocation(processNumber, proResNum, "初始已分配资源");
            //查看最大需求资源
            LookMax(processNumber, proResNum);
            //下一步提示
            Console.WriteLine("任意键开始分配资源...");
            Console.ReadKey();
            //查看最多需求
            LookMax(processNumber, proResNum);
            Console.WriteLine("************************分配开始******************************");
            //按顺序判断是否满足条件
            for (int i = 0; i < processNumber; i++)
            {
                Console.Write("进程{0}：", i + 1);
                PrintProcess(SysRes.Max, i, "最大需求（Max）");//打印当前进程最大需求
                PrintProcess(SysRes.Allocation, i, "已经分配资源（Allocation）");//打印当前进程已经分配                            
                LookAvailable("当前系统总资源");//查看分配后系统资源数
                bool re = IsMeet(i);//是否满足条件
                if (re)
                {
                    //查看系统总资源
                    LookAvailable();
                    //查看分配资源
                    LookAllocation(processNumber, proResNum);
                }
                else
                {
                    //请求资源
                    bool r = RequestResource(i);
                    if (r)
                    {
                        ReleaseResource(i);//释放资源
                        Console.WriteLine("《《《《《《《《《《《《《《《《《《《《进程{0}执行完毕", i + 1);
                    }
                    else
                    {
                        //不满足条件，添加到队列中下次执行
                        processList.Add(i);
                        Console.WriteLine("《《《《《《《《《《《《《《《《《《《《进程{0}放在进程列表里，等待资源够了再分配", i + 1);

                    }
                }

                Console.WriteLine("本次分配结束");
                LookAvailable("当前系统总资源");//查看分配后系统资源数
                PrintNextHint();
                Console.ReadKey();
                if (i==processNumber-1)//循环完毕，判断是否有为执行的进程
                {
                    if (processList.Count > 0)
                    {
                        Console.WriteLine("#########还有进程#########");
                        Console.WriteLine("#########回退执行#########");
                        //把i值调到改进程
                        i =processList.First();
                        //从列表中移除
                        processList.Remove(i);
                    }
                }
                
            }
            Console.WriteLine("系统结束");
            Console.ReadKey();

        }
        /// <summary>
        /// 初始化数据
        /// </summary>
        /// <param name="processNumber"></param>
        /// <param name="proResNum"></param>
        public static void InitData(int processNumber, int proResNum)
        {
            //初始化随机分配资源，为了演示，只分配总资源的1/2
            for (int i = 0; i < processNumber; i++)
            {
                int[] re = DefaultAllocation();
                for (int j = 0; j < proResNum; j++)
                {
                    //随机分配资源
                    SysRes.Allocation[i, j] = re[j];
                    //分配资源后，减去系统已有资源数
                    SysRes.Available[j] = SysRes.Available[j] - re[j];
                }
            }
        }
        public static void PrintProcess(int[,] processArray, int processNumber, string hint)
        {
            Console.WriteLine(hint);
            for (int i = 0; i < processArray.GetLength(1); i++)
            {
                Console.Write(processArray[processNumber, i] + "\t");
            }
            Console.WriteLine("\n-------------------------------");
        }
        /// <summary>
        /// 释放资源
        /// </summary>
        public static void ReleaseResource(int process)
        {
            for (int i = 0; i < SysRes.Allocation.GetLength(1); i++)
            {
                int a = SysRes.Allocation[process, i];
                int b = SysRes.Available[i];
                SysRes.Available[i] = a + b;//恢复系统资源
                SysRes.Allocation[process, i] = 0;//分配资源设置为0

            }
            Console.WriteLine("资源释放完毕！");
        }
        /// <summary>
        /// 请求资源
        /// </summary>
        public static bool RequestResource(int processNumber)
        {
            Console.WriteLine("请求资源");
            //请求资源=Max-Allocation
            int[,] max = SysRes.Max;
            int[,] all = SysRes.Allocation;
            int[] available = SysRes.Available;//系统已经有的资源
            int[] request = new int[max.GetLength(1)];
            bool isMeet = false;
            for (int i = 0; i < max.GetLength(1); i++)
            {
                int a = max[processNumber, i];
                int b = all[processNumber, i];
                request[i] = a - b;
                if (request[i] <= available[i])
                {
                    //满足
                    isMeet = true;
                }
                else
                {
                    //不满足，下一个进程
                    isMeet = false;
                }
            }
            LookAvailable("当前系统资源");
            //判断是否超过系统已经有的资源
            if (isMeet)
            {
                //分配资源
                for (int i = 0; i < max.GetLength(1); i++)
                {
                    //修改请求资源矩阵
                    SysRes.Request[processNumber, i] = request[i];
                    SysRes.Allocation[processNumber, i] += request[i];//分配资源=已经分配的资源+请求资源
                    int a = request[i];
                    int b = SysRes.Available[i];
                    SysRes.Available[i] = b - a;//扣除系统总资源
                }
                LookAvailable("分配后系统资源");
                //输出请求资源
                PrintProcess(SysRes.Request, processNumber, "请求分配的资源");
                Console.WriteLine("资源已分配");
                return true;
            }
            else
            {
                Console.WriteLine("请求资源数超过系统未分配资源数，分配失败");
                return false;
            }

        }
        public static bool IsMeet(int process)
        {
            //判断已经有的资源是否满足最大需求
            int[,] max = SysRes.Max;
            int[,] all = SysRes.Allocation;
            bool isMeet = false;
            for (int i = 0; i < max.GetLength(1); i++)
            {
                if (max[process, i] <= all[process, i])
                {
                    isMeet = true;
                }
                else
                {
                    isMeet = false;
                }
            }
            if (isMeet)
            {
                Console.WriteLine("满足条件释放资源");
                ReleaseResource(process);//释放资源
                return true;
            }
            else
            {
                Console.WriteLine("不满足条件，请求资源");
                return false;
            }
        }
        public static void PrintNextHint()
        {
            Console.WriteLine("************************************************");
            Console.WriteLine("***************按任意键执行下一步***************");
            Console.WriteLine("************************************************");
            Console.ReadKey();
        }
        public static void LookRequest(int processNumber, int proResNum, string hint = "请求资源")
        {
            Console.WriteLine(hint + "（Request）");
            int[,] request = SysRes.Request;
            for (int i = 0; i < processNumber; i++)
            {
                for (int j = 0; j < proResNum; j++)
                {
                    Console.Write(request[i, j] + "\t");
                }
                Console.WriteLine();
            }
            Console.WriteLine("\n------------------------");

        }
        public static void LookMax(int processNumber, int proResNum, string hint = "最大需求资源")
        {
            Console.WriteLine(hint + "（Max）");
            int[,] max = SysRes.Max;
            for (int i = 0; i < processNumber; i++)
            {
                Console.Write("进程{0}：", i + 1);
                for (int j = 0; j < proResNum; j++)
                {
                    Console.Write(max[i, j] + "\t");
                }
                Console.WriteLine();
            }
            Console.WriteLine("\n------------------------");
        }
        /// <summary>
        /// 查看已经分配的资源
        /// </summary>
        /// <param name="processNumber">进程数</param>
        /// <param name="proResNum">进程需要的资源种类</param>
        public static void LookAllocation(int processNumber, int proResNum, string hint = "已分配资源")
        {
            //查看分配后的已经分配资源
            Console.WriteLine(hint + "（Allocation）");
            for (int i = 0; i < processNumber; i++)
            {
                Console.Write("进程{0}：", i + 1);
                for (int j = 0; j < proResNum; j++)
                {
                    Console.Write(SysRes.Allocation[i, j] + "\t");
                }
                Console.WriteLine();
            }
            Console.WriteLine("\n------------------------");
        }
        /// <summary>
        /// 只用于初始化分配，只分配总资源数的1/2
        /// </summary>
        /// <returns></returns>
        public static int[] DefaultAllocation()
        {
            int a, b, c;
            //获取系统资源数
            int[] available = SysRes.Available;
            //生成随机数，范围为0到资源数的开区间，所有+1
            a = new Random().Next(available[0] / 2);
            b = new Random().Next(available[1] / 2);
            c = new Random().Next(available[2] / 2);
            return new int[] { a, b, c };
        }
        public static int[] GetRandom()
        {
            int a, b, c;
            //获取系统资源数
            int[] available = SysRes.Available;
            //生成随机数，范围为0到资源数的开区间，所有+1
            a = new Random().Next(available[0] + 1);
            b = new Random().Next(available[1] + 1);
            c = new Random().Next(available[2] + 1);
            return new int[] { a, b, c };
        }
        /// <summary>
        /// 查看系统资源
        /// </summary>
        public static void LookAvailable(string hint = "系统总资源数")
        {
            //系统总资源数
            Console.WriteLine(hint + "（Available）");
            foreach (var item in SysRes.Available)
            {
                Console.Write(item + "\t");
            }

            Console.WriteLine("\n-----------------------------------");
        }
    }
}
